package Monitor;

import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import common.events.PeerDataRequest;
import common.events.PeerDataResponse;
import common.peer.PeerAddress;
import common.simulation.PeerStatus;

import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Handler;
import se.sics.kompics.Positive;
import se.sics.kompics.Start;
import se.sics.kompics.Stop;
import se.sics.kompics.address.Address;
import se.sics.kompics.network.Network;
import se.sics.kompics.network.Transport;
import se.sics.kompics.timer.CancelPeriodicTimeout;
import se.sics.kompics.timer.SchedulePeriodicTimeout;
import se.sics.kompics.timer.Timer;

/**
 * The <code>CyclonMonitorClient</code> class.
 * 
 * @author Cosmin Arad <cosmin@sics.se>
 * @version $Id$
 */
public class MonitorClient extends ComponentDefinition {

	Positive<PeerStatus> peerStatus = positive(PeerStatus.class);
	Positive<Network> network = positive(Network.class);
	Positive<Timer> timer = positive(Timer.class);

	private Logger logger;

	private UUID sendViewTimeoutId;

	private Address monitorServerAddress;

	private PeerAddress self;

	private long updatePeriod;

	private Transport protocol;

	public MonitorClient() {
		subscribe(handleInit, control);
		subscribe(handleStart, control);
		subscribe(handleStop, control);

		subscribe(handleChangeUpdatePeriod, network);
		subscribe(handlePeerDataResponse, peerStatus);
		subscribe(handleSendView, timer);
	}

	private Handler<MonitorClientInit> handleInit = new Handler<MonitorClientInit>() {
		public void handle(MonitorClientInit event) {
			self = event.getSelf();
			updatePeriod = event.getConfiguration().getClientUpdatePeriod();
			monitorServerAddress = event.getConfiguration()
					.getMonitorServerAddress();
			protocol = event.getConfiguration().getProtocol();

			logger = LoggerFactory.getLogger(getClass().getName() + "@"
					+ self.getPeerId());
		}
	};

	private Handler<Start> handleStart = new Handler<Start>() {
		public void handle(Start event) {
			SchedulePeriodicTimeout spt = new SchedulePeriodicTimeout(
					updatePeriod, updatePeriod);
			spt.setTimeoutEvent(new SendView(spt));
			sendViewTimeoutId = spt.getTimeoutEvent().getTimeoutId();

			trigger(spt, timer);
		}
	};

	private Handler<Stop> handleStop = new Handler<Stop>() {
		public void handle(Stop event) {
			trigger(new CancelPeriodicTimeout(sendViewTimeoutId), timer);
		}
	};

	private Handler<ChangeUpdatePeriod> handleChangeUpdatePeriod = new Handler<ChangeUpdatePeriod>() {
		public void handle(ChangeUpdatePeriod event) {
			updatePeriod = event.getNewUpdatePeriod();
			trigger(new CancelPeriodicTimeout(sendViewTimeoutId), timer);
			SchedulePeriodicTimeout spt = new SchedulePeriodicTimeout(
					updatePeriod, updatePeriod);
			spt.setTimeoutEvent(new SendView(spt));
			sendViewTimeoutId = spt.getTimeoutEvent().getTimeoutId();
			trigger(spt, timer);
		}
	};

	private Handler<SendView> handleSendView = new Handler<SendView>() {
		public void handle(SendView event) {
			logger.debug("SEND_VIEW");

			PeerDataRequest request = new PeerDataRequest();
			trigger(request, peerStatus);
		}
	};

	private Handler<PeerDataResponse> handlePeerDataResponse = new Handler<PeerDataResponse>() {
		public void handle(PeerDataResponse event) {
			logger.debug("CYCLON_NEIGHBORS_RESP");
			if (event.getData().getSelf() != null) {
				// only send notification to the server if the peer has joined
				TManNeighborsNotification viewNotification = new TManNeighborsNotification(
						self, monitorServerAddress, protocol, event
								.getData());
				trigger(viewNotification, network);
			}
		}
	};
}
